﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DLC.Module.DataTable
{
    public class DataTableManager : IDataTableManager
    {
        private readonly Dictionary<TypeNamePair, object> _dataTables = new Dictionary<TypeNamePair, object>();

        //public DataTableManager()
        //{
        //}
        public IDataTableRowType AddDataTable(IDataTableRowType dataTable)// where T : class, IDataRow
        {
            var typeNamePair = new TypeNamePair(dataTable.GetDataTableRowType());
            if (HasDataTable(typeNamePair))
            {
                throw new SystemException($"Already exist data table '{typeNamePair}'.");
            }
            _dataTables.Add(typeNamePair, dataTable);
            return dataTable;
        }

        public IDataTableRowType AddDataTable(string name, IDataTableRowType dataTable)// where T : class, IDataRow
        {
            var typeNamePair = new TypeNamePair(dataTable.GetDataTableRowType(), name);
            if (HasDataTable(typeNamePair))
            {
                throw new SystemException($"Already exist data table '{typeNamePair}'.");
            }
            _dataTables.Add(typeNamePair, dataTable);
            return dataTable;
        }

        public IDataTable<T> AddDataTable<T>(IDataTable<T> dataTable) where T : class, IDataRow
        {
            var typeNamePair = new TypeNamePair(typeof(T));
            if (HasDataTable(typeNamePair))
            {
                throw new SystemException($"Already exist data table '{typeNamePair}'.");
            }
            _dataTables.Add(typeNamePair, dataTable);
            return dataTable;
        }

        public IDataTable<T> AddDataTable<T>(string name, IDataTable<T> dataTable) where T : class, IDataRow
        {
            var typeNamePair = new TypeNamePair(typeof(T), name);
            if (HasDataTable(typeNamePair))
            {
                throw new SystemException($"Already exist data table '{typeNamePair}'.");
            }
            _dataTables.Add(typeNamePair, dataTable);
            return dataTable;
        }

        public bool RemoveDataTable<T>() where T : IDataRow
        {
            return RemoveDataTable(new TypeNamePair(typeof(T)));
        }

        public bool RemoveDataTable(Type dataRowType)
        {
            if (dataRowType == null)
            {
                throw new NullReferenceException("Data row type is invalid.");
            }
            if (!typeof(IDataRow).IsAssignableFrom(dataRowType))
            {
                throw new NotSupportedException($"Data row type '{dataRowType.FullName}' is invalid.");
            }
            return RemoveDataTable(new TypeNamePair(dataRowType));
        }

        public bool RemoveDataTable<T>(string name) where T : IDataRow
        {
            return RemoveDataTable(new TypeNamePair(typeof(T), name));
        }

        public bool RemoveDataTable(Type dataRowType, string name)
        {
            if (dataRowType == null)
            {
                throw new NullReferenceException("Data row type is invalid.");
            }
            if (!typeof(IDataRow).IsAssignableFrom(dataRowType))
            {
                throw new NotSupportedException($"Data row type '{dataRowType.FullName}' is invalid.");
            }
            return RemoveDataTable(new TypeNamePair(dataRowType, name));
        }

        public bool RemoveDataTable<T>(IDataTable<T> dataTable) where T : IDataRow
        {
            if (dataTable == null)
            {
                throw new ArgumentNullException("Data table is invalid.");
            }

            return RemoveDataTable(new TypeNamePair(typeof(T), dataTable.Name));
        }

        public bool HasDataTable<T>() where T : IDataRow
        {
            return HasDataTable(new TypeNamePair(typeof(T)));
        }

        public bool HasDataTable(Type dataRowType)
        {
            if (dataRowType == null)
            {
                throw new NullReferenceException("Data row type is invalid.");
            }
            if (!typeof(IDataRow).IsAssignableFrom(dataRowType))
            {
                throw new NotSupportedException($"Data row type '{dataRowType.FullName}' is invalid.");
            }
            return HasDataTable(new TypeNamePair(dataRowType));
        }

        public bool HasDataTable<T>(string name) where T : IDataRow
        {
            return HasDataTable(new TypeNamePair(typeof(T), name));
        }

        public bool HasDataTable(Type dataRowType, string name)
        {
            if (dataRowType == null)
            {
                throw new NullReferenceException("Data row type is invalid.");
            }
            if (!typeof(IDataRow).IsAssignableFrom(dataRowType))
            {
                throw new NotSupportedException($"Data row type '{dataRowType.FullName}' is invalid.");
            }
            return HasDataTable(new TypeNamePair(dataRowType, name));
        }

        public IDataTable<T> GetDataTable<T>() where T : IDataRow
        {
            return GetDataTable<T>(new TypeNamePair(typeof(T)));
        }

        public IDataTable<T> GetDataTable<T>(string name) where T : IDataRow
        {
            return GetDataTable<T>(new TypeNamePair(typeof(T), name));
        }
        private bool RemoveDataTable(TypeNamePair typeNamePair)
        {
            if (_dataTables.ContainsKey(typeNamePair))
            {
                return _dataTables.Remove(typeNamePair);
            }

            return false;
        }
        private bool HasDataTable(TypeNamePair typeNamePair)
        {
            return _dataTables.ContainsKey(typeNamePair);
        }

        private IDataTable<T> GetDataTable<T>(TypeNamePair typeNamePair) where T : IDataRow
        {
            object dataTable = null;
            if (_dataTables.TryGetValue(typeNamePair, out dataTable))
            {
                return dataTable as IDataTable<T>;
            }

            return null;
        }

        public IDataTable<T>[] GetDataTables<T>() where T : IDataRow
        {
            List<IDataTable<T>> datas = new List<IDataTable<T>>();
            foreach (var data in _dataTables)
            {
                if (data.Key.Type.Equals(typeof(T)))
                {
                    datas.Add(data.Value as IDataTable<T>);
                }
            }
            return datas.ToArray();
        }

        public IDataTableRowType[] GetDataTables(string name)
        {
            List<IDataTableRowType> datas = new List<IDataTableRowType>();
            foreach (var data in _dataTables)
            {
                if (data.Key.Name.Equals(name))
                {
                    datas.Add(data.Value as IDataTableRowType);
                }
            }
            return datas.ToArray();
        }

        public IDataTableRowType[] GetDataTables(Type type)
        {
            List<IDataTableRowType> datas = new List<IDataTableRowType>();
            foreach (var data in _dataTables)
            {
                if (data.Key.Type.Equals(type))
                {
                    datas.Add(data.Value as IDataTableRowType);
                }
            }
            return datas.ToArray();
        }
    }
}
